package com.zhao.algorithm.common

import java.lang
import java.sql.{Connection, PreparedStatement, ResultSet, SQLException, Statement}

import com.alibaba.fastjson.JSONObject
import com.zhao.algorithm.utils.TimeTransformUtil
import redis.clients.jedis.Jedis

/**
 * Description: <br/>
 * Copyright (c) ，2020 ， 赵 <br/>
 * This program is protected by copyright laws. <br/>
 * Date： 2020/11/23 10:30
 *
 * @author 柒柒
 * @version : 1.0
 */

object Common {


  /**
   * 获取当前日志的时间戳作为value
   * 没有获取的或者失败,则用当前时间
   * @param td log中的timestamp
   * @return Long类型的时间戳
   */
  def getTime(td: String): Long ={
    var time = 0L
    if (!td.startsWith("2020")){
      time = TimeTransformUtil.logDateToStamp(td)
    }else if(td.startsWith("2020")){
      time = TimeTransformUtil.dateToStamp(td)
    }else{
      time = TimeTransformUtil.getLongTime()
    }
    time
  }



  /**
   * 判断是否达到阈值,到达阈值之后,准备写入Mysql
   *
   * @param jedis  redis连接
   * @param key 查询的key
   * @param threshold 阈值步长
   * @return
   */
  def isThreshold(jedis: Jedis, key: String, threshold: Int): Boolean ={

    jedis.scard(key) % threshold == 0
  }



  /**
   * 判断key是PV的是否存在,不存在则写入 value每次+1, 并设置过期时间
   *
   * @param jedis redis 连接
   * @param key 插入的key
   * @return
   */
  def addPV2Redis(jedis: Jedis, key: String) ={

    val status: lang.Long = jedis.incrBy(key, 1)

    //如果插入成功,则更新过期时间
    if (status == 1){
      jedis.expire(key,Constant.OVERDUE_TIME)
    }
  }


  /**
   * 判断key是UV的是否存在,不存在则写入 结果为集合,并设置过期时间
   * @param jedis
   * @param key
   * @param value
   * @return
   */
  def addUV2Redis(jedis: Jedis, key: String, value: String) = {
    jedis.sadd(key,value)

    jedis.expire(key,Constant.OVERDUE_TIME)
  }


  /**
   * 查询某 tid_gid 对应的其他action的pv
   * @param jedis redis连接
   * @param key 要查询的key
   * @param action 分类
   * @return
   */
  def getOtherPV(jedis: Jedis, key: String, action: String): Int = {
    var keyLong = 0
    try {
      if (jedis.exists(key + action)) {
        keyLong = jedis.get(key + action).toInt
      }
    } catch {
      case e: Exception =>{
        throw new Exception("从Redis获取PV时出现异常",e)
      }
    }
    keyLong
  }



  /**
   * 查询某 tid_gid 对应的其他action的uv
   * @param jedis redis连接
   * @param key 要查询的key
   * @param action 分类
   * @return
   */
  def getOtherUV(jedis: Jedis, key: String, action: String): Int = {
    var keyLong = 0
    try {
      if (jedis.exists(key + action)) {
        //scard 返回集合中元素的数量
        keyLong = jedis.scard(key + action).toInt
      }
    } catch {
      case e: Exception =>{
        throw new Exception("从Redis获取UV时出现异常",e)
      }
    }
    keyLong
  }


  /**
   * 用redis保存指定key做flush id
   *
   * @param jedis redis连接
   * @return
   */
  def buildFlushID(jedis: Jedis):Int={
    jedis.incrBy("calculator:flushID",1).toInt
  }


  /**
   * 构建JSON
   * @param flush_id 推送id
   * @param `type` 推送类型 曝光uv = 1 浏览uv = 2 分享pv = 3 分享uv = 4 webuv = 5
   */
  def buildJSON(flush_id: Int, `type`: Int, tid: Int, gid: Int, dtime: Int,
                exp_pv: Int, exp_uv: Int,
                browse_pv: Int, browse_uv: Int,
                browse_all_pv: Int, browse_all_uv: Int, // 新增
                share_pv: Int, share_uv: Int,
                web_pv: Int, web_uv: Int, // 改名
                waken_pv: Int, waken_uv: Int // 改名
               ) = {

    val obj = new JSONObject()
    obj.put("flush_id", flush_id)
    obj.put("type", `type`)
    obj.put("tid", tid)
    obj.put("gid", gid)
    obj.put("dtime", dtime)
    obj.put("exp_pv", exp_pv)
    obj.put("exp_uv", exp_uv)
    obj.put("browse_pv", browse_pv)
    obj.put("browse_uv", browse_uv)

    obj.put("browse_all_pv", browse_all_pv)
    obj.put("browse_all_uv", browse_all_uv)
    obj.put("share_pv", share_pv)
    obj.put("share_uv", share_uv)
    obj.put("web_pv", web_pv)
    obj.put("web_uv", web_uv)
    obj.put("waken_pv", waken_pv)
    obj.put("waken_uv", waken_uv)

    // 构造整体JSON
    val obj2 = new JSONObject()

    obj2.put("path", "/push/consumer")
    obj2.put("type", 81)
    obj2.put("body", obj.toString())

    obj2.toString()
  }


  /**
   * 构建JSON
   * @param `type` 推送类型 曝光uv = 1 浏览uv = 2 分享pv = 3 分享uv = 4 webuv = 5
   * @param tid 帖子id
   * @param gid 城市id
   * @param dtime unix time
   * @param now_arr 该tid，gid，本次插入的key
   * @param last_arr 该tid，gid，对应mysql上次插入的最晚的一次的value数据，然后计算增量
   * @return
   */
  def buildJSON(`type`: Int, tid: Int, gid: Int, dtime: Int, now_arr: Array[Int], last_arr: Array[Int]): String = {

    val obj = new JSONObject()
    // 4个常量值
    obj.put("type", `type`)
    obj.put("tid", tid)
    obj.put("gid", gid)
    obj.put("dtime", dtime)
    // 12个本次值
    obj.put("exp_pv", now_arr(0))
    obj.put("exp_uv", now_arr(1))
    obj.put("web_pv", now_arr(2))
    obj.put("web_uv", now_arr(3))
    obj.put("browse_pv", now_arr(4))
    obj.put("browse_uv", now_arr(5))
    obj.put("browse_all_pv", now_arr(6))
    obj.put("browse_all_uv", now_arr(7))
    obj.put("share_pv", now_arr(8))
    obj.put("share_uv", now_arr(9))
    obj.put("waken_pv", now_arr(10))
    obj.put("waken_uv", now_arr(11))
    obj.put("flush_id", now_arr(12))
    // 12个上次插入的值 与本次的差值
    obj.put("exp_pv_inc", now_arr(0) - last_arr(0))
    obj.put("exp_uv_inc", now_arr(1) - last_arr(1))
    obj.put("web_pv_inc", now_arr(2) - last_arr(2))
    obj.put("web_uv_inc", now_arr(3) - last_arr(3))
    obj.put("browse_pv_inc", now_arr(4) - last_arr(4))
    obj.put("browse_uv_inc", now_arr(5) - last_arr(5))
    obj.put("browse_all_pv_inc", now_arr(6) - last_arr(6))
    obj.put("browse_all_uv_inc", now_arr(7) - last_arr(7))
    obj.put("share_pv_inc", now_arr(8) - last_arr(8))
    obj.put("share_uv_inc", now_arr(9) - last_arr(9))
    obj.put("waken_pv_inc", now_arr(10) - last_arr(10))
    obj.put("waken_uv_inc", now_arr(11) - last_arr(11))


    // 构造整体JSON
    val obj2 = new JSONObject()

    obj2.put("path", "/push/consumer")
    obj2.put("type", 81)
    obj2.put("body", obj.toString())

    obj2.toString()
  }

  /**
   * 根据tid gid,获取本次即将写入mysql的所有其他行为类型的value
   * @param jedis redis连接
   * @param tid_gid
   * @return 结果数组
   */
  def getOtherPVUV(jedis: Jedis, tid_gid: String): Array[Int] = {

    val exp_pv: Int = Common.getOtherPV(jedis, tid_gid, Constant.EXPTID_PV)
    val exp_uv: Int = Common.getOtherUV(jedis, tid_gid, Constant.EXPTID_UV)
    val web_pv: Int = Common.getOtherPV(jedis, tid_gid, Constant.OBROWSE_PV)
    val web_uv: Int = Common.getOtherUV(jedis, tid_gid, Constant.OBROWSE_UV)
    val browse_pv: Int = Common.getOtherPV(jedis, tid_gid, Constant.BROWSE_PV)
    val browse_uv: Int = Common.getOtherUV(jedis, tid_gid, Constant.BROWSE_UV)
    val browse_all_pv: Int = Common.getOtherPV(jedis, tid_gid, Constant.BROWSE_ALL_PV)
    val browse_all_uv: Int = Common.getOtherUV(jedis, tid_gid, Constant.BROWSE_ALL_UV)
    val share_pv: Int = Common.getOtherPV(jedis, tid_gid, Constant.SHARE_PV)
    val share_uv: Int = Common.getOtherUV(jedis, tid_gid, Constant.SHARE_UV)
    val waken_pv: Int = Common.getOtherPV(jedis, tid_gid, Constant.WAKEN_PV)
    val waken_uv: Int = Common.getOtherUV(jedis, tid_gid, Constant.WAKEN_UV)
    val flushID = Common.buildFlushID(jedis)

    val arr: Array[Int] = Array(exp_pv, exp_uv, web_pv, web_uv, browse_pv, browse_uv, browse_all_pv, browse_all_uv, share_pv, share_uv, waken_pv, waken_uv, flushID)

    arr

  }


  /**
   * 根据 tid gid,按照flush_id 在对应的表中进行查找,返回最后一组数据
   * @param connection jdbc连接
   * @param sql 查询的sql语句
   * @param tid 帖子id
   * @param gid 城市id
   * @return
   */
  def getLastPVUV(connection: Connection, sql: String, tid: Int, gid: Int): Array[Int] = {
    val statement: Statement = connection.createStatement()

    var last_exp_pv = 0
    var last_exp_uv = 0
    var last_web_pv = 0
    var last_web_uv = 0
    var last_browse_pv = 0
    var last_browse_uv = 0
    var last_browse_all_pv = 0
    var last_browse_all_uv = 0
    var last_share_pv = 0
    var last_share_uv = 0
    var last_waken_pv = 0
    var last_waken_uv = 0

    try {
      val res: ResultSet = statement.executeQuery(sql)
      while (res.next()) {
        last_exp_pv = res.getInt("exp_pv")
        last_exp_uv = res.getInt("exp_uv")
        last_web_pv = res.getInt("web_pv")
        last_web_uv = res.getInt("web_uv")
        last_browse_pv = res.getInt("browse_pv")
        last_browse_uv = res.getInt("browse_uv")
        last_browse_all_pv = res.getInt("browse_all_pv")
        last_browse_all_uv = res.getInt("browse_all_uv")
        last_share_pv = res.getInt("share_pv")
        last_share_uv = res.getInt("share_uv")
        last_waken_pv = res.getInt("waken_pv")
        last_waken_uv = res.getInt("waken_uv")
      }
    } catch {
      case e: SQLException => {
        throw new SQLException("读取MySQL中最近一次PV,UV异常", e)
      }
    } finally {
      if (statement != null) {
        statement.close()
      }
    }

    val arr2: Array[Int] = Array(last_exp_pv, last_exp_uv, last_web_pv, last_web_uv, last_browse_pv, last_browse_uv, last_browse_all_pv, last_browse_all_uv, last_share_pv, last_share_uv, last_waken_pv, last_waken_uv)

    arr2

  }


  /**
   * 写入mysql的sql语句以及填充函数 封装起来
   * @param connection jdbc连接
   * @param sql sql模板
   * @param tid 帖子id
   * @param gid 城市id
   * @param log_time 日志中的时间
   * @param now_arr 同一tid，gid 其他value 数组形式
   * @param last_arr 同一tid，gid 上次插入mysql的 数组形式
   * @return jdbc对象
   */
  def execInsertSql(connection: Connection, sql: String, tid: Int, gid: Int, log_time: Int, now_arr: Array[Int], last_arr: Array[Int]): AnyVal = {
    // 创建 PreparedStatement 对象，执行 update sql 使用
    val statement: PreparedStatement = connection.prepareStatement(sql)

    try {
      statement.setInt(1, tid)
      statement.setInt(2, gid)
      statement.setInt(3, log_time)
      // 获取该tid gid 本次数据，然后写入数据库
      statement.setInt(4, now_arr(0))
      statement.setInt(5, now_arr(1))
      statement.setInt(6, now_arr(2))
      statement.setInt(7, now_arr(3))
      statement.setInt(8, now_arr(4))
      statement.setInt(9, now_arr(5))
      statement.setInt(10, now_arr(6))
      statement.setInt(11, now_arr(7))
      statement.setInt(12, now_arr(8))
      statement.setInt(13, now_arr(9))
      statement.setInt(14, now_arr(10))
      statement.setInt(15, now_arr(11))
      // 获取该tid gid 上次最近一次插入的数据，计算差值
      statement.setInt(16, now_arr(0) - last_arr(0))
      statement.setInt(17, now_arr(1) - last_arr(1))
      statement.setInt(18, now_arr(2) - last_arr(2))
      statement.setInt(19, now_arr(3) - last_arr(3))
      statement.setInt(20, now_arr(4) - last_arr(4))
      statement.setInt(21, now_arr(5) - last_arr(5))
      statement.setInt(22, now_arr(6) - last_arr(6))
      statement.setInt(23, now_arr(7) - last_arr(7))
      statement.setInt(24, now_arr(8) - last_arr(8))
      statement.setInt(25, now_arr(9) - last_arr(9))
      statement.setInt(26, now_arr(10) - last_arr(10))
      statement.setInt(27, now_arr(11) - last_arr(11))
      // 获取本次的flush_id
      statement.setInt(28, now_arr(12))

      // 执行 insert 语句
      statement.executeUpdate()

    } catch {
      case e: SQLException =>{
        throw new SQLException("读取MySQL中最近一次PV,UV异常", e)
      }
    } finally {
      // 这里不关闭连接，会造成慢性oom异常（慢性：和insert的次数有直接关系，如果insert多，oom时间就快）
      if (statement != null) {
        statement.close()
      }
    }

  }


}
